<?php
define('SEO_EXTENSION','.html');
define('SEO_EXTENSION_SEPARATOR','-');
/**
 * 页面静态化处理类
 * @package
 * @author ZhouHaoHua <53027327@qq.com>
 */
class PageStatic {
    /**
     * 网站的根目录组
     * @var array 包括主站、简体站、繁体站、移动版网站等
     */
    private static $rootDirList = array(Z_APP);

    /**
     * 取得不可删除的文件夹
     * @return array
     */
    public static function getNotDelDir(){
        $can_not_del_dir = array(
            basename(rtrim(Z_APP, '/')),
            'core',
            'ext',
            'images',
            'log'
        );

        return $can_not_del_dir;
    }
    /**
     * headers标题头设置
     * 缓存时间原则：有生成了静态页的可以适当减小缓存时间，另外此方法应用于控制器开头
     * @param string $mod        页面的唯一标识字符串传控制器名称和方法名称就可以了。如：$this->mod.__FUNCTION__
     * @param int    $maxPageAge 最大生命周期秒
     */
    public static function headers($mod, $maxPageAge = 0) {
        $mod .= $_SERVER['SCRIPT_FILENAME'] . $_SERVER['QUERY_STRING'] . (isset($_GET) ? implode(',', $_GET) : '');
        $ExpiresTag = substr(time(), 0, -((int) strlen($maxPageAge)));
        $ExpiresTag .= $mod;
        if ((int) $maxPageAge) {
            header("Expires: " . date("D, d M Y H:i:s", (time() + $maxPageAge)) . " GMT");
            header("Cache-Control: max-age=" . $maxPageAge);
            PageStatic::etagCache($ExpiresTag);
        } else {
            header("Expires: " . date("D, d M Y H:i:s", 0) . " GMT");
            header("Cache-Control: no-cache, must-revalidate");
            header("Pragma: no-cache");
        }
    }

    /**
     * eTag标签处理
     * @param string $ExpiresTag eTage字符串
     */
    public static function etagCache($ExpiresTag) {
        //echo $ExpiresTag;exit;
        $ExpiresTag = md5($ExpiresTag);
        if ($_SERVER["HTTP_IF_NONE_MATCH"] == $ExpiresTag) {
            if (config::DEBUG === true) {//Z_DEBUG
                $text = date("Y-m-d H:i:s") . "\n";
                $text .= $_SERVER["HTTP_USER_AGENT"] . "\n";
                file_put_contents(Z_LOG_DIR . 'test_cache.txt', $text); //测试日志
            }
            header("Etag: " . $ExpiresTag, true, 304);
            header("HTTP/1.1 304 Not Modified");
            exit(0);
        } else {
            header("Etag: " . $ExpiresTag);
            header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
        }
    }

    /**
     * 创建静态的html页面到网站根目录
     * 要生成静态页面必须满足：一、当前url地址不能带?；二、常量CACHE_CREATE_HTML为1；三、控制器文件定义了true值的$USE_PAGE_CACHE；四、不能是404或403等页面
     * @param string      $htmlCodes      要写的文件内容
     * @param string      $content        模板名称如404
     * @param bool|string $USE_PAGE_CACHE 在控制器controller文件夹下面的文件中定义的是否真的要生成html页面的变量
     * @return bool
     */
    public static function createHtml($htmlCodes, $content, $USE_PAGE_CACHE = false) {
        //根据当前环境判断来创建简体、繁体或手机网站
        $dir      = Z_DIR;
        $fileName = '';
        /**
         * 在html代码末尾的小标签，让它知道是哪个环节生成的！
         */
        $_Tag = '<!--ZHH-->';

        //只有80端口可以生成静态页
        if ($_SERVER['SERVER_PORT'] != '80' || $USE_PAGE_CACHE !== true) {
            return false;
        }
        $content = preg_replace('/\..+/', '', $content);
        if (strpos($_SERVER['REQUEST_URI'], '?') === false &&
            strpos($_SERVER['REQUEST_URI'], SEO_EXTENSION_SEPARATOR) === false &&
            $content != '404' && $content != '403'
        ) {
            $_array = explode('/', $_SERVER['REQUEST_URI']);
            if (sizeof($_array) <= 2) { //网站根目录下的文件【根目录下面的页面必须是.html文件】
                $_Tag     = '<!--首页和产品详细页-->';
                $fileName = preg_replace('@^/@', '', $_SERVER['REQUEST_URI']);
                $fileName = $fileName ? $fileName : 'index' . SEO_EXTENSION; //网站首页文件和有.html的文件
                if (!preg_match('@' . preg_quote(SEO_EXTENSION) . '$@', $fileName)) {
                    unset($fileName);
                }
            } elseif (preg_match('/^\/.+\/$/', $_SERVER['REQUEST_URI'])) { //网站子孙后代目录下的文件 如：/jianadalvyou/packages/ => /jianadalvyou/packages/index.html
                $_d = $dir . $_SERVER['REQUEST_URI'];
                if (file_exists($_d) || @mkdir($_d, 0755, false)) { //禁止创建嵌套目录，因为如果允许嵌套的话，会有些上级目录因找不到页面而出现403错误！
                    $_Tag     = '<!--目录各级首页-->';
                    $dir      = $_d;
                    $fileName = 'index' . SEO_EXTENSION;
                } else {
                    return false;
                }
            } elseif (preg_match('/^\/.+' . preg_quote(SEO_EXTENSION) . '$/', $_SERVER['REQUEST_URI'])) { //明确指定的某个文件的创建，如：/news/search.html。
                $_d = $dir . preg_replace('/^\//', '', $_SERVER['REQUEST_URI']);
                //创建所需要的目录，并指定要创建的文件名
                $b_dir = dirname($_d);

                $do = true;
                /*if ((int) $current_category_id && !file_exists($b_dir . '/index.html')) { //若在产品列表页面则要判断该目录下面是否有了index.html文件，如果有就可以生成，没有就不要生成。
                    $do = false;
                }*/
                if ($do === true) {
                    $_Tag = '<!--明确指定的某个文件的创建-->';
                    if (file_exists($b_dir) || @mkdir($b_dir, 0755, false)) {
                        $dir      = $b_dir . '/';
                        $fileName = basename($_d);
                    }
                }
            }
            if ($fileName && $dir && $dir == Z_DIR) {
                $full_file = $dir .'/'. $fileName;
                self::writeFile($full_file, $htmlCodes . $_Tag);
            }
        }
        //new Exception_User($_SERVER['REQUEST_URI'].'|'.CACHE_CREATE_HTML.'|'.$USE_PAGE_CACHE.'|'.$content, 'test');
    }

    /**
     * 写内容到文件
     * @param string $fileName  文件名
     * @param string $htmlCodes 文件内容
     */
    public static function writeFile($fileName, $htmlCodes) {
        if (strpos($fileName, '.php') !== false) {
            if (!@file_put_contents($fileName, $htmlCodes, LOCK_EX)) {
                echo '<script>alert("Can\'t write ' . $fileName . ' plx check Permission!");</script>';
            }
            //如果因磁盘空间等原因导致写不入数据则要删除该文件
            if (@filesize($fileName) < 1) {
                @unlink($fileName);
            }
        }
    }

    /**
     * 清除生成的静态文件
     * @param string $dir_file 指定的文件名，如果为空则删除根目录下的apache生成的所有html文件。只接受当前网站下的子路径，如meidonglvyou/或luoshanji-shengtamonika-1d-lvyou.html
     * @param int    $aga_time 有效期，默认是一小时3600
     * @return int 返回成功删除数
     */
    public static function clearHtml($dir_file = '', $aga_time = 3600) {
        $n = 0;
        foreach (self::$rootDirList as $_dir) {
            if (file_exists($_dir) && is_dir($_dir)) {
                if ($dir_file == '') { //删除根目录
                    if ($_action = self::_cleanHtmlFiles($_dir, $aga_time)) {
                        $n++;
                    }
                } elseif (is_file($_dir . $dir_file)) { //文件删除
                    $f = $_dir . $dir_file;
                    if ($_action = self::deleteDirOrFile($f)) {
                        $n++;
                    }
                } elseif (is_dir($_dir . $dir_file)) { //删除非根目录
                    $d = $_dir . $dir_file;
                    if ($_action = self::_cleanHtmlFiles($d, $aga_time)) {
                        //删除成功后要删除目录本身
                        @rmdir($d);
                        $n++;
                    }
                }
            }
        }
        return $n;
    }

    /**
     * 删除整个目录下面的静态html文件
     * 私有
     * @param string $path     路径
     * @param int    $aga_time 文件有效期
     * @return array or false
     */
    private static function _cleanHtmlFiles($path = "dirname/", $aga_time = 0) {
        $data = false;
        $d    = dir($path);
        $time = time();
        while (false !== ($v = $d->read())) {
            if ($v == "." || $v == "..") {
                continue;
            }
            $file = $d->path . $v;
            //echo $file.':'.filemtime($file)."\n";
            if (!(int) $aga_time || (filemtime($file) + $aga_time < $time)) {
                //echo $file.'|'.$v.'|'.SEO_EXTENSION."\n";
                if (is_file($file) && (strpos($v, SEO_EXTENSION) !== false || strpos($v, '.') === false)) { //本站SEO_EXTENSION是.html
                    $data[] = self::deleteDirOrFile($file, array(SEO_EXTENSION));
                } elseif (is_dir($file)) {
                    $data[] = self::deleteDirOrFile($file, array());
                }
            }
        }

        $d->close();
        return $data;
    }

    /**
     * 判断目录是否为空
     * @param $dir
     * @return bool|void
     */
    private static function checkEmpty($dir) {
        if (!is_dir($dir))
            return;
        $hand = opendir($dir);
        $mark = true;
        while ($file = readdir($hand)) {
            if ($file != '.' && $file != '..') {
                $mark = false;
                break;
            }
        }
        closedir($hand);
        return $mark;
    }

    /**
     * 删除一个文件或递归删除一个目录
     * 慎用此方法，小心你全部文件被删除！
     * @param string $str 文件或目录的路径
     * @param array  $exp 文件扩展名，默认是删除.html文件，如果为空的话则是文件都删除！
     * @return bool
     */
    private static function deleteDirOrFile($str, $exp = array('.html')) {
        //安全检查
        if (self::blacklist_check($str) !== true) {
            return false;
        }
        if (is_file($str) && (!$exp || in_array(arrays::strtolower(substr($str, strrpos($str, '.'))), arrays::strtolower($exp)))) {
            $owner = posix_getpwuid(fileowner($str));
            if (in_array($owner['name'], array('apache', 'daemon'))) { //文件属主必须是apache的才能删除，要不然很危险！
                return @unlink($str);
            }
            if (self::checkEmpty(dirname($str))) {
                rmdir(dirname($str));
            }
        } elseif (is_dir($str)) {
            /*
            $scan = glob(rtrim($str, '/') . '/*');
            foreach ($scan as $index => $path) {
                self::deleteDirOrFile($path, $exp);
            }
            */
            $owner = posix_getpwuid(fileowner($str));
            if (in_array($owner['name'], array('apache', 'daemon'))) { //目录属主必须是apache的才能删除，要不然很危险！
                //return @rmdir($str);
                if ($str != '/' && $str != '') { //只能删除当前网站下面的目录。self::blacklist_check($str)中已经做了检查，这里不用再检查
                    //先改名后删除，别说是脱裤子打屁，如果功能没问题就别改以下代码。简单的谁都想用，就是遇到了问题才用以下方法的。
                    //'rename CD CD001 /tmp/CD';
                    $dir_array = explode('/', $str);
                    $oldKey    = $dir_array[sizeof($dir_array) - 1];
                    unset($dir_array[sizeof($dir_array) - 1]);
                    $newKey = $oldKey . 'ZhhOldDir' . mt_rand(1000, 9999);
                    $cmd    = 'rename ' . $oldKey . ' ' . $newKey . ' ' . $str;
                    exec(escapeshellcmd($cmd));

                    $newStr = implode('/', $dir_array) . '/' . $newKey;
                    $cmd    = 'rm -Rf ' . $newStr;
                    exec(escapeshellcmd($cmd));
                }
            }
        }
    }

    /**
     * 禁止删除的文件夹检查
     * @param string $dir_or_file 检查的文件名
     * @return true|false 允许删就返回true否则就返回false
     */
    private static function blacklist_check($dir_or_file) {
        $can_do = false;
        //1.只有绝对路径的文件才能删除；
        if (strpos($dir_or_file, './') !== false || strpos($dir_or_file, '..') !== false) {
            return false;
        }
        //2.文件名目录中在当前站目录下的文件才能删除DIR_FS_CATALOG,DIR_FS_CATALOG_CN,DIR_FS_CATALOG_TW
        $sub_dir = '';
        foreach (self::$rootDirList as $root_dir) {
            if (strpos($dir_or_file, $root_dir) !== false) {
                $can_do  = true;
                $sub_dir = preg_replace('/\/.+/', '', str_replace($root_dir, '', $dir_or_file));
                break;
            }
        }
        //3.当前站目录下的不能删除的文件夹
        if (in_array($sub_dir, self::getNotDelDir())) {
            $can_do = false;
        }
        //var_dump($sub_dir, $can_do); exit;
        return $can_do;
    }
}
